package cn.maihe.elg.operation.supports.electronicseal.ms;

import cfca.sadk.algorithm.sm2.SM2PrivateKey;
import cfca.sadk.util.CertUtil;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.x509.certificate.X509Cert;
import cn.maihe.elg.operation.model.bo.GuaranteeTemplatePdfParams;
import cn.maihe.elg.operation.model.enums.GuaranteeTemplateType;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDO;
import cn.maihe.elg.operation.repository.entity.ProjectInfoDO;
import cn.maihe.elg.operation.supports.electronicseal.ms.config.MSSignatureConfig;
import cn.maihe.elg.operation.supports.system.ElgResourceService;
import cn.maihe.elg.operation.utils.DocFillUtil;
import com.alibaba.fastjson.JSON;
import com.epoint.ofd.bizlogic.EPOFDUtils;
import com.epoint.ofd.bizlogic.OFDUtils;
import com.epoint.ofd.domain.SignMode;
import com.epoint.ofd.domain.SignRequest;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.UUID;

@Slf4j
@Component
public class MSOFDSignSupport {
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(),
            x9ECParameters.getG(), x9ECParameters.getN());

    @Resource
    private ElgResourceService elgResourceService;

    @Resource
    private MSSignatureConfig msSignatureConfig;

    @Value("${resourcesDir}")
    private String resourcesDir;

    public Path getSignatureFile(AcceptOrderInfoDO acceptOrderInfoDO,
                                 ProjectInfoDO projectInfoDO,
                                 GuaranteeTemplatePdfParams pdfParams,
                                 boolean restoreFlag){
        try {
            log.info("msSignatureConfig:{}", JSON.toJSONString(msSignatureConfig));
            String initAddr = msSignatureConfig.getInitAddr();
            String password = msSignatureConfig.getSm2FilePwd();
            EPOFDUtils.initAddr(initAddr);

            String acceptOrderNo = acceptOrderInfoDO.getAcceptOrderNo();
            //未签章pdf文件存储路径
            Path targetPdfPath;
            if (projectInfoDO.getProjectEncryptionFlag() == 2) {
                targetPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo() + "_unSeal", "pdf");
            } else {
                targetPdfPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo(), "pdf");
            }
            //担保保函模板填充后docx文件
            Path sourceDocxPath = elgResourceService.buildGuaranteeFileByTemplate(acceptOrderNo, pdfParams);
            //docx转pdf
            DocFillUtil.doc2pdf(sourceDocxPath, targetPdfPath, null);
            this.deleteTmpFile(sourceDocxPath);

            EPOFDUtils.convertPDF2OFD(targetPdfPath.toAbsolutePath().toString());
            this.deleteTmpFile(targetPdfPath);

            Path targetSealOfdPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo(), "ofd");
            String ofdPath = targetSealOfdPath.toAbsolutePath().toString();
            log.info("MSOFDSignSupport ofdPath:{}", ofdPath);
            if (projectInfoDO.getProjectEncryptionFlag() == 2) {
                if (!restoreFlag) {
                    Path targetSealOfdPathTemp = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo() + "_original", "ofd");
                    log.info("MSOFDSignSupport targetSealOfdPathTemp:{}", targetSealOfdPathTemp.toAbsolutePath());
                    Files.copy(targetSealOfdPathTemp, targetSealOfdPath, StandardCopyOption.REPLACE_EXISTING);
                }

                Path unSealTargetOfdPath = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo() + "_unSeal", "ofd");
                EPOFDUtils.MergeOfd(unSealTargetOfdPath.toAbsolutePath().toString(), targetSealOfdPath.toAbsolutePath().toString());
                this.deleteTmpFile(unSealTargetOfdPath);
            }

            GuaranteeTemplateType guaranteeTemplateType = pdfParams.getGuaranteeTemplateType();
            Integer signPageNum;
            Float signOrgXpx;
            Float signOrgYpx;
            if (projectInfoDO.getProjectEncryptionFlag() == 2) {
                signPageNum = guaranteeTemplateType.getSignPageNum() * 2;
                signOrgXpx = guaranteeTemplateType.getSignOrgXpx();
                signOrgYpx = guaranteeTemplateType.getSignOrgYpx();
            } else {
                signPageNum = guaranteeTemplateType.getSignPageNum();
                signOrgXpx = guaranteeTemplateType.getSignOrgXpx();
                signOrgYpx = guaranteeTemplateType.getSignOrgYpx();
            }

            String sm2File = resourcesDir + "/54e9c099fbabeaa39d598d34eb8c69536af82f5c-sign.sm2";
            X509Cert cert = CertUtil.getCertFromSM2(sm2File);
            /** 1、准备请求实体 */

            String fileid = UUID.randomUUID().toString();
            SignRequest signRequest = new SignRequest();
            /** 签名文件名称 */
            log.info("MSOFDSignSupport fileName:{}", targetSealOfdPath.getFileName());
            signRequest.setFilename(targetSealOfdPath.getFileName().toString());
            signRequest.setFileid(fileid);
            /** 签名页码 */

            signRequest.setPage(signPageNum);
            /** 签名坐标 */

            signRequest.setX(signOrgXpx.intValue());
            signRequest.setY(signOrgYpx.intValue());

            /** 印章大小 */
            signRequest.setHeight(40);
            signRequest.setWidth(40);

            /** 设置印章文件 */
            signRequest.setSealfile(java.util.Base64.getEncoder().encodeToString(OFDUtils.fileToBytes(resourcesDir + "/泉州丰泽得兴融资担保有限公司.esl")));
            /**
             * 设置保护模式，如果是
             * （1）ContinueSign就是可以持续签章的。
             * （2）如果是WholeProtected，则下次不能签章，
             * （3）如果是EpointMergeSign，则用在ofd合并的场景。盖章后合并另一个明文ofd，验签任然可以成功。
             */
            signRequest.setSignMode(SignMode.EpointMergeSign);
            Certificate signCert = new org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory()
                    .engineGenerateCertificate(new ByteArrayInputStream(cert.getEncoded()));
            byte[] expSigVal = null;
            signRequest.setSigVal(java.util.Base64.getEncoder().encodeToString(expSigVal));
            // 解析ofd获取待签名数据
            String strtosign = EPOFDUtils.gettosign(ofdPath, signRequest);
            signRequest.setToSign(strtosign);
            SM2PrivateKey gmtPrivateKey = KeyUtil.getPrivateKeyFromSM2(sm2File, password);
            // 私钥D
            String priHex = new String(Hex.encode(gmtPrivateKey.getDByBytes())).toLowerCase();
            // 待验签原文
            byte[] src = java.util.Base64.getDecoder().decode(strtosign.split("@")[0]);
            // 构造私钥
            ECPrivateKeyParameters priKey = new ECPrivateKeyParameters(new BigInteger(1, ByteUtils.fromHexString(priHex)), ecDomainParameters);
            SM2Signer signer = new SM2Signer();
            ParametersWithRandom pwr = new ParametersWithRandom(priKey, new SecureRandom());
            signer.init(true, pwr);
            signer.update(src, 0, src.length);
            // 签名
            expSigVal = signer.generateSignature();
            signRequest.setSigVal(java.util.Base64.getEncoder().encodeToString(expSigVal));
            /** 放入ofd */
            EPOFDUtils.signlocal(ofdPath, signRequest);

            if (projectInfoDO.getProjectEncryptionFlag() == 1) {
                Path targetSealOfdPathTemp = elgResourceService.buildSaveAbsolutePath("ofd", acceptOrderNo, acceptOrderInfoDO.getGuaranteeNo() + "_original", "ofd");
                Files.copy(targetSealOfdPath, targetSealOfdPathTemp, StandardCopyOption.REPLACE_EXISTING);
            }
            return targetSealOfdPath;
        } catch (Exception e) {
            log.error("眉山签章异常:{}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    private void deleteTmpFile(Path... tmpPath) {
        if (tmpPath != null && tmpPath.length != 0) {
            Arrays.stream(tmpPath).forEach(tmp -> {
                try {
                    Files.deleteIfExists(tmp);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}
